//
// (C) Copyright 2009 Irantha Suwandarathna (irantha@gmail.com)
// All rights reserved.
//

/* Copyright (c) 1995-2000, The Hypersonic SQL Group.
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Hypersonic SQL Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * on behalf of the Hypersonic SQL Group.
 *
 *
 * For work added by the HSQL Development Group:
 *
 * Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Globalization;
using System.Collections.Generic;
using System.Text;
using EffiProz.Core.Lib;
using EffiProz.Core.DataTypes;
using EffiProz.Core.Resources;
using EffiProz.Core.Rights;
using EffiProz.Core.Indexes;

namespace EffiProz.Core
{
    /**
     * Script generation.
     *
     * The core functionality of this class was inherited from Hypersonic and
     * extensively rewritten and extended _in successive versions of HSQLDB.<p>
     *
     * @author Thomas Mueller (Hypersonic SQL Group)
     * @author fredt@users
     * @version 1.8.0
     * @since 1.7.0
     */
    public class DatabaseScript
    {

        /**
         * Returns the DDL and all other statements for the database excluding
         * INSERT and SET <tablename> READONLY statements.
         * cachedData == true indicates that SET <tablename> INDEX statements should
         * also be included.
         *
         * This class should not have any dependencies on metadata reporting.
         */
        public static Result getScript(Database database, bool indexRoots)
        {
           
            Result r = Result.newSingleColumnResult("COMMAND", Types.VARCHAR);

            r.metaData.tableNames[0] = "SYSTEM_SCRIPT";

            // collation for database
            if (database.collation.name != null)
            {
                string name =
                    StringConverter.toQuotedString(database.collation.name, '"',
                                                   true);

                addRow(r, "SET DATABASE COLLATION " + name);
            }

            // Role definitions
       
            

            foreach (var role in database.getGranteeManager().getRoleNames())
            {
               

                // ADMIN_ROLE_NAME is not persisted
                if (!GranteeManager.DBA_ADMIN_ROLE_NAME.Equals(role))
                {
                    addRow(r, "CREATE ROLE " + role);
                }
            }

            // aliases
            Dictionary<string,string> h = database.getAliasMap();
            Dictionary<string, string> builtin = Library.getAliasMap();

           

            foreach (var alias in h.Keys)
            {
             
                string java = (String)h[(alias)];
                string biJava = (String)builtin[(alias)];

                if (biJava != null && biJava.Equals(java))
                {
                    continue;
                }

                StringBuilder buffer = new StringBuilder(64);

                buffer.Append(Token.T_CREATE).Append(' ').Append(
                    Token.T_ALIAS).Append(' ');
                buffer.Append(alias);
                buffer.Append(" FOR \"");
                buffer.Append(java);
                buffer.Append('"');
                addRow(r, buffer.ToString());
            }

            addSchemaStatements(database, r, indexRoots);

            // rights for classes, tables and views
            addRightsStatements(database, r);

            if (database.logger.hasLog())
            {
                int delay = database.logger.getWriteDelay();
                bool millis = delay < 1000;

                if (millis)
                {
                    if (delay != 0 && delay < 20)
                    {
                        delay = 20;
                    }
                }
                else
                {
                    delay /= 1000;
                }

                string statement = "SET WRITE_DELAY " + delay + (millis ? " MILLIS"
                                                                        : "");

                addRow(r, statement);
            }

            return r;
        }

        public static void addSchemaStatements(Database database, Result r,
                                        bool indexRoots)
        {

           
            foreach (var schemaKey in database.schemaManager.userSchemaNameIterator())
            {
             
                QNameManager.QName schema =
                    database.schemaManager.toSchemaHsqlName(schemaKey);
                IList<Table> tTable =
                    database.schemaManager.getTables(schema.name);
                List<Constraint> forwardFK = new List<Constraint>();

                // schema creation
                {
                    string ddl = getSchemaCreateDDL(database, schema);

                    addRow(r, ddl);
                }

                // sequences
                /*
                         CREATE SEQUENCE <name>
                         [AS {INTEGER | BIGINT}]
                         [START WITH <value>]
                         [INCREMENT BY <value>]
                 */
           

                foreach (var seq in database.schemaManager.sequenceIterator(schema.name))
                {
                   
                    StringBuilder a = new StringBuilder(128);

                    a.Append(Token.T_CREATE).Append(' ');
                    a.Append(Token.T_SEQUENCE).Append(' ');
                    a.Append(seq.getName().statementName).Append(' ');
                    a.Append(Token.T_AS).Append(' ');
                    a.Append(Types.getTypeString(seq.getType())).Append(' ');
                    a.Append(Token.T_START).Append(' ');
                    a.Append(Token.T_WITH).Append(' ');
                    a.Append(seq.peek()).Append(' ');

                    if (seq.getIncrement() != 1)
                    {
                        a.Append(Token.T_INCREMENT).Append(' ');
                        a.Append(Token.T_BY).Append(' ');
                        a.Append(seq.getIncrement()).Append(' ');
                    }

                    addRow(r, a.ToString());
                }

                // tables
                for (int i = 0, tSize = tTable.Count; i < tSize; i++)
                {
                    Table t = (Table)tTable[i];

                    if (t.isView())
                    {
                        continue;
                    }

                    StringBuilder a = new StringBuilder(128);

                    getTableDDL(database, t, i, forwardFK, false, a);
                    addRow(r, a.ToString());

                    // indexes for table
                    for (int j = 1; j < t.getIndexCount(); j++)
                    {
                        IndexAVL index = t.getIndex(j);

                        if (QNameManager.QName.isReservedName(index.getName().name))
                        {

                            // the following are autocreated with the table
                            // indexes for primary keys
                            // indexes for unique constraints
                            // own table indexes for foreign keys
                            continue;
                        }

                        a = new StringBuilder(64);

                        a.Append(Token.T_CREATE).Append(' ');

                        if (index.isUnique())
                        {
                            a.Append(Token.T_UNIQUE).Append(' ');
                        }

                        a.Append(Token.T_INDEX).Append(' ');
                        a.Append(index.getName().statementName);
                        a.Append(' ').Append(Token.T_ON).Append(' ');
                        a.Append(t.getName().statementName);

                        int[] col = index.getColumns();
                        int len = index.getVisibleColumns();

                        getColumnList(t, col, len, a);
                        addRow(r, a.ToString());
                    }

                    // _readonly for TEXT tables only
                    if (t.isText() && t.isConnected() && t.isDataReadOnly())
                    {
                        a = new StringBuilder(64);

                        a.Append(Token.T_SET).Append(' ').Append(
                            Token.T_TABLE).Append(' ');
                        a.Append(t.getName().statementName);
                        a.Append(' ').Append(Token.T_READONLY).Append(' ').Append(
                            Token.T_TRUE);
                        addRow(r, a.ToString());
                    }

                    // data source
                    string dataSource = getDataSource(t);

                    if (dataSource != null)
                    {
                        addRow(r, dataSource);
                    }

                    // header
                    string header = getDataSourceHeader(t);

                    if (!indexRoots && header != null)
                    {
                        addRow(r, header);
                    }

                    // triggers
                    int numTrigs = TriggerConstants.NUM_TRIGS;

                    for (int tv = 0; tv < numTrigs; tv++)
                    {
                        List<TriggerDef> trigVec = t.triggerLists[tv];

                        if (trigVec == null)
                        {
                            continue;
                        }

                        int trCount = trigVec.Count;

                        for (int k = 0; k < trCount; k++)
                        {
                            a = ((TriggerDef)trigVec[k]).getDDL();

                            addRow(r, a.ToString());
                        }
                    }
                }

                // forward referencing foreign keys
                for (int i = 0, tSize = forwardFK.Count; i < tSize; i++)
                {
                    Constraint c = (Constraint)forwardFK[i];
                    StringBuilder a = new StringBuilder(128);

                    a.Append(Token.T_ALTER).Append(' ').Append(
                        Token.T_TABLE).Append(' ');
                    a.Append(c.getRef().getName().statementName);
                    a.Append(' ').Append(Token.T_ADD).Append(' ');
                    getFKStatement(c, a);
                    addRow(r, a.ToString());
                }

                // SET <tablename> INDEX statements
                Session sysSession = database.sessionManager.getSysSession();

                for (int i = 0, tSize = tTable.Count; i < tSize; i++)
                {
                    Table t = tTable[i];

                    if (indexRoots && t.isIndexCached()
                            && !t.isEmpty(sysSession))
                    {
                        addRow(r, getIndexRootsDDL(t));
                    }
                }

                // RESTART WITH <value> statements
                for (int i = 0, tSize = tTable.Count; i < tSize; i++)
                {
                    Table t = tTable[i];

                    if (!t.isTemp())
                    {
                        string ddl = getIdentityUpdateDDL(t);

                        addRow(r, ddl);
                    }
                }

                // views
                for (int i = 0, tSize = tTable.Count; i < tSize; i++)
                {
                    Table t = tTable[i];

                    if (t.isView())
                    {
                        View v = (View)t;
                        StringBuilder a = new StringBuilder(128);

                        a.Append(Token.T_CREATE).Append(' ').Append(
                            Token.T_VIEW).Append(' ');
                        a.Append(v.getName().statementName).Append(' ').Append(
                            '(');

                        int count = v.getColumnCount();

                        for (int j = 0; j < count; j++)
                        {
                            a.Append(v.getColumn(j).columnName.statementName);

                            if (j < count - 1)
                            {
                                a.Append(',');
                            }
                        }

                        a.Append(')').Append(' ').Append(Token.T_AS).Append(' ');
                        a.Append(v.getStatement());
                        addRow(r, a.ToString());
                    }
                }
            }
        }

        public static string getIdentityUpdateDDL(Table t)
        {

            if (t.identityColumn == -1)
            {
                return "";
            }
            else
            {
                string tablename = t.getName().statementName;
                string colname =
                    t.getColumn(t.identityColumn).columnName.statementName;
                long idval = t.identitySequence.peek();
                StringBuilder a = new StringBuilder(128);

                a.Append(Token.T_ALTER).Append(' ').Append(Token.T_TABLE).Append(
                    ' ').Append(tablename).Append(' ').Append(
                    Token.T_ALTER).Append(' ').Append(Token.T_COLUMN).Append(
                    ' ').Append(colname).Append(' ').Append(
                    Token.T_RESTART).Append(' ').Append(Token.T_WITH).Append(
                    ' ').Append(idval);

                return a.ToString();
            }
        }

        public static string getIndexRootsDDL(Table t)
        {

            StringBuilder a = new StringBuilder(128);

            a.Append(Token.T_SET).Append(' ').Append(Token.T_TABLE).Append(' ');
            a.Append(t.getName().statementName);
            a.Append(' ').Append(Token.T_INDEX).Append('\'');
            a.Append(t.getIndexRoots());
            a.Append('\'');

            return a.ToString();
        }

        public static string getSchemaCreateDDL(Database database, QNameManager.QName schemaName)
        {

            StringBuilder ab = new StringBuilder(128);

            ab.Append(Token.T_CREATE).Append(' ');
            ab.Append(Token.T_SCHEMA).Append(' ');
            ab.Append(schemaName.statementName).Append(' ');
            ab.Append(Token.T_AUTHORIZATION).Append(' ');
            ab.Append(GranteeManager.DBA_ADMIN_ROLE_NAME);

            return ab.ToString();
        }

        public static void getTableDDL(Database database, Table t, int i,
                                List<Constraint> forwardFK, bool useSchema,
                                StringBuilder a)
        {

            a.Append(Token.T_CREATE).Append(' ');

            if (t.isTemp())
            {
                a.Append(Token.T_GLOBAL).Append(' ');
                a.Append(Token.T_TEMPORARY).Append(' ');
            }

            if (t.isText())
            {
                a.Append(Token.T_TEXT).Append(' ');
            }
            else if (t.isCached())
            {
                a.Append(Token.T_CACHED).Append(' ');
            }
            else
            {
                a.Append(Token.T_MEMORY).Append(' ');
            }

            a.Append(Token.T_TABLE).Append(' ');

            if (useSchema)
            {
                a.Append(t.getName().schema.statementName).Append('.');
            }

            a.Append(t.getName().statementName);
            a.Append('(');

            int columns = t.getColumnCount();
            int[] pk = t.getPrimaryKey();
            QNameManager.QName pkName = null;
            Constraint pkConst = t.getPrimaryConstraint();

            if (pkConst != null && !pkConst.getName().isReservedName())
            {
                pkName = pkConst.getName();
            }

            for (int j = 0; j < columns; j++)
            {
                Column column = t.getColumn(j);
                string colname = column.columnName.statementName;

                a.Append(colname);
                a.Append(' ');

                string sType = Types.getTypeString(column.getType());

                a.Append(sType);

                // append the size and scale if > 0
                bool hasSize = false;

                if (column.getType() == Types.TIMESTAMP)
                {
                    if (column.getSize() != 6)
                    {
                        hasSize = true;
                    }
                }
                else
                {
                    hasSize = column.getSize() > 0;
                }

                if (hasSize)
                {
                    a.Append('(');
                    a.Append(column.getSize());

                    if (column.getScale() > 0)
                    {
                        a.Append(',');
                        a.Append(column.getScale());
                    }

                    a.Append(')');
                }

                string defaultstring = column.getDefaultDDL();

                if (defaultstring != null)
                {
                    a.Append(' ').Append(Token.T_DEFAULT).Append(' ');
                    a.Append(defaultstring);
                }

                if (j == t.getIdentityColumn())
                {
                    a.Append(" GENERATED BY DEFAULT AS IDENTITY(START WITH ");
                    a.Append(column.identityStart);

                    if (column.identityIncrement != 1)
                    {
                        a.Append(Token.T_COMMA).Append(Token.T_INCREMENT).Append(
                            ' ').Append(Token.T_BY).Append(' ');
                        a.Append(column.identityIncrement);
                    }

                    a.Append(")");
                }

                if (!column.isNullable())
                {
                    a.Append(' ').Append(Token.T_NOT).Append(' ').Append(
                        Token.T_NULL);
                }

                if ((pk.Length == 1) && (j == pk[0]) && pkName == null)
                {
                    a.Append(' ').Append(Token.T_PRIMARY).Append(' ').Append(
                        Token.T_KEY);
                }

                if (j < columns - 1)
                {
                    a.Append(',');
                }
            }

            if (pk.Length > 1 || (pk.Length == 1 && pkName != null))
            {
                a.Append(',');

                if (pkName != null)
                {
                    a.Append(Token.T_CONSTRAINT).Append(' ');
                    a.Append(pkName.statementName).Append(' ');
                }

                a.Append(Token.T_PRIMARY).Append(' ').Append(Token.T_KEY);
                getColumnList(t, pk, pk.Length, a);
            }

            Constraint[] v = t.getConstraints();

            for (int j = 0, vSize = v.Length; j < vSize; j++)
            {
                Constraint c = v[j];

                switch (c.getType())
                {

                    case Constraint.UNIQUE:
                        a.Append(',').Append(Token.T_CONSTRAINT).Append(' ');
                        a.Append(c.getName().statementName);
                        a.Append(' ').Append(Token.T_UNIQUE);

                        int[] col = c.getMainColumns();

                        getColumnList(c.getMain(), col, col.Length, a);
                        break;

                    case Constraint.FOREIGN_KEY:

                        // forward referencing FK
                        Table maintable = c.getMain();
                        int maintableindex =
                            database.schemaManager.getTableIndex(maintable);

                        if (maintableindex > i)
                        {
                            forwardFK.Add(c);
                        }
                        else
                        {
                            a.Append(',');
                            getFKStatement(c, a);
                        }
                        break;

                    case Constraint.CHECK:
                        try
                        {
                            a.Append(',').Append(Token.T_CONSTRAINT).Append(' ');
                            a.Append(c.getName().statementName);
                            a.Append(' ').Append(Token.T_CHECK).Append('(');
                            a.Append(c.core.check.getDDL());
                            a.Append(')');
                        }
                        catch (CoreException)
                        {

                            // should not throw as it is already tested OK
                        }
                        break;
                }
            }

            a.Append(')');

            if (t.onCommitPreserve)
            {
                a.Append(' ').Append(Token.T_ON).Append(' ');
                a.Append(Token.T_COMMIT).Append(' ').Append(Token.T_PRESERVE);
                a.Append(' ').Append(Token.T_ROWS);
            }
        }

        /**
         * Generates the SET TABLE <tablename> SOURCE <string> statement for a
         * text table;
         */
        public static string getDataSource(Table t)
        {

            string dataSource = t.getDataSource();

            if (dataSource == null)
            {
                return null;
            }

            bool isDesc = t.isDescDataSource();
            StringBuilder a = new StringBuilder(128);

            a.Append(Token.T_SET).Append(' ').Append(Token.T_TABLE).Append(' ');
            a.Append(t.getName().statementName);
            a.Append(' ').Append(Token.T_SOURCE).Append(' ').Append('"');
            a.Append(dataSource);
            a.Append('"');

            if (isDesc)
            {
                a.Append(' ').Append(Token.T_DESC);
            }

            return a.ToString();
        }

        /**
         * Generates the SET TABLE <tablename> SOURCE HEADER <string> statement for a
         * text table;
         */
        public static string getDataSourceHeader(Table t)
        {

            string header = t.getHeader();

            if (header == null)
            {
                return null;
            }

            StringBuilder a = new StringBuilder(128);

            a.Append(Token.T_SET).Append(' ').Append(Token.T_TABLE).Append(' ');
            a.Append(t.getName().statementName);
            a.Append(' ').Append(Token.T_SOURCE).Append(' ');
            a.Append(Token.T_HEADER).Append(' ');
            a.Append(header);

            return a.ToString();
        }

        /**
         * Generates the column definitions for a table.
         */
        private static void getColumnList(Table t, int[] col, int len,
                                          StringBuilder a)
        {

            a.Append('(');

            for (int i = 0; i < len; i++)
            {
                a.Append(t.getColumn(col[i]).columnName.statementName);

                if (i < len - 1)
                {
                    a.Append(',');
                }
            }

            a.Append(')');
        }

        /**
         * Generates the foreign key declaration for a given Constraint object.
         */
        private static void getFKStatement(Constraint c, StringBuilder a)
        {

            a.Append(Token.T_CONSTRAINT).Append(' ');
            a.Append(c.getName().statementName);
            a.Append(' ').Append(Token.T_FOREIGN).Append(' ').Append(Token.T_KEY);

            int[] col = c.getRefColumns();

            getColumnList(c.getRef(), col, col.Length, a);
            a.Append(' ').Append(Token.T_REFERENCES).Append(' ');
            a.Append(c.getMain().getName().statementName);

            col = c.getMainColumns();

            getColumnList(c.getMain(), col, col.Length, a);

            if (c.getDeleteAction() != Constraint.NO_ACTION)
            {
                a.Append(' ').Append(Token.T_ON).Append(' ').Append(
                    Token.T_DELETE).Append(' ');
                a.Append(getFKAction(c.getDeleteAction()));
            }

            if (c.getUpdateAction() != Constraint.NO_ACTION)
            {
                a.Append(' ').Append(Token.T_ON).Append(' ').Append(
                    Token.T_UPDATE).Append(' ');
                a.Append(getFKAction(c.getUpdateAction()));
            }
        }

        /**
         * Returns the foreign key action rule.
         */
        private static string getFKAction(int action)
        {

            switch (action)
            {

                case Constraint.CASCADE:
                    return Token.T_CASCADE;

                case Constraint.SET_DEFAULT:
                    return Token.T_SET + ' ' + Token.T_DEFAULT;

                case Constraint.SET_NULL:
                    return Token.T_SET + ' ' + Token.T_NULL;

                default:
                    return Token.T_NO + ' ' + Token.T_ACTION;
            }
        }

        /**
         * Adds a script line to the result.
         */
        private static void addRow(Result r, string sql)
        {

            if (sql == null || sql.Length == 0)
            {
                return;
            }

            String[] s = new String[1];

            s[0] = sql;

            r.add(s);
        }

        /**
         * Generates the GRANT statements for grantees.
         *
         * When views is true, generates rights for views only. Otherwise generates
         * rights for tables and classes.
         *
         * Does not generate script for:
         *
         * grant on builtin classes to public
         * grant select on system tables
         *
         */
        private static void addRightsStatements(Database dDatabase, Result r)
        {

            StringBuilder a;
            SortedDictionary<string,User> userlist = dDatabase.getUserManager().getUsers();
        
            GranteeManager gm = dDatabase.getGranteeManager();
            IEnumerable< Grantee> grantees = gm.getGrantees();

            foreach (var u in userlist.Values)
            {
           
                string name = u.getName();

                // PUBLIC user is not persisted.  (However, his
                // grants/revokes are).  _SYSTEM user not _in user list.
                if (!name.Equals(Token.T_PUBLIC))
                {
                    addRow(r, u.getCreateUserDDL());
                }
            }

            // grantees has ALL Users and Roles, incl. hidden and reserved ones.
            // Therefore, we filter _out the non-persisting ones.
            foreach (var g in grantees )
            {
              
                string name = g.getName();

                // _SYSTEM user, DBA Role grants/revokes not persisted
                if (name.Equals("_SYSTEM") || name.Equals("DBA"))
                {
                    continue;
                }

                string rolestring = g.allRolesString();

                if (rolestring != null)
                {
                    addRow(r, "GRANT " + rolestring + " TO " + name);
                }

                Dictionary<object,int> rightsmap = g.getRights();

                if (rightsmap == null)
                {
                    continue;
                }

            

                foreach (var nameobject in rightsmap.Keys)
                {

                    int right = 0;
                    rightsmap.TryGetValue(nameobject, out right);

                    a = new StringBuilder(64);

                    a.Append(Token.T_GRANT).Append(' ');
                    a.Append(GranteeManager.getRightsList(right));
                    a.Append(' ').Append(Token.T_ON).Append(' ');

                    if (nameobject is String)
                    {
                        if (nameobject.Equals("java.lang.Math")
                                || nameobject.Equals("org.hsqldb.Library"))
                        {
                            continue;
                        }

                        a.Append("CLASS \"");
                        a.Append((String)nameobject);
                        a.Append('\"');
                    }
                    else
                    {
                        QNameManager.QName hsqlname = (QNameManager.QName)nameobject;

                        // assumes all non string objects are table names
                        Table table = dDatabase.schemaManager.findUserTable(null,
                            hsqlname.name, hsqlname.schema.name);

                        // either table != null or is system table
                        if (table != null)
                        {
                            a.Append(hsqlname.schema.statementName).Append(
                                '.').Append(hsqlname.statementName);
                        }
                        else
                        {
                            continue;
                        }
                    }

                    a.Append(' ').Append(Token.T_TO).Append(' ');
                    a.Append(g.getName());
                    addRow(r, a.ToString());
                }
            }
        }
    }
}